_Die_AMPEL_ARD_v1.ino
#include <Wire.h> // I2C Interfave Library
#include <extEEPROM.h>
#include <ds3231.h> // RTC Module Library
#include <GyverTimers.h> // Timer interrupts Library
#include "_Die_AMPEL_ARD_v1.h"
#include "Memory.h"
#include "Lampen.h"
#include "RTC.h"
#include "ESP_12F.h"
#include "FAN.h"
//#define button 12 // TEMPORARY FOR CONNECTION DEBUGGING
//uint8_t sent = 0; // TEMPORARY FOR CONNECTION DEBUGGING
/********************************** VARIABLES **********************************/
/******** Status Variables ********/
status_flags_t Status;
/******** RTC and Time variables ********/
struct ts RTC_time;
uint16_t ticks_upd_clock = CLOCK_SYNCH_TIME_PERIOD; // Tick counter for Clock Timer
my_clock_t Clock;
/******** Lamps variables ********/
ampel_t Ampel;
uint16_t ticks_ampel = AMPEL_MAIN_TIME; // Tick counter for Lamp Timer (Main States)
char POST_Request[] = {0,0,0,0,0}; // For storing the POST Request content from ESP-01
/******** Variables for connectivity ********/
ESP_12f_t ESP_Modul;
char Rec_Line[255]; // The buffer for data received from ESP-01
uint8_t i = 0;
/******** Variables for EEPROM ********/
my_time_t EEPROM_time;
extEEPROM EEPROM_Board(kbits_16, 1, 16); // device size, number of devices, page size
/******** Variables for FAN ********/
FAN_t Fan;
/****************************** FUNCTION PROTOTYPES ******************************/
void Lamp_Timer_Routine(status_flags_t* Status, ampel_t* Ampel);
void Pins_Routine(ampel_t* Ampel);
void UART_Routine(status_flags_t* Status, char* Line, bool Silent);
void Time_Routine(status_flags_t* Status, my_clock_t* Clock, my_time_t* EEPROM_time, ts* RTC_time);
//void Fan_Routine(status_flags_t* Status);
void setup()
{
pinMode(13, OUTPUT); // Indicating LED (debug purpouses)
Wire.begin();
Serial.begin(115200); // Debug port (programming connector)
Serial1.begin(9600); // ESP_1 UART (RX1, TX1)
DS3231_init(0);
init_ampel(&Ampel); // Initialize Ampel variable. just saving space here
init_clock(&Clock); // Initialize Clock variable. just saving space here
/**** FAN Start ****/
FAN_Start(&Status, &Fan); // The function uses delay();
Serial.println("FAN Started");
/**** Setting Connection with ESP-01 ****/
ESP_12F_struct_Init(&ESP_Modul);
ESP_12F_Connect(&ESP_Modul, &Status);
if(Status.ESP_01_connected == 1)
{
Serial.println("ESP-01 Connected!");
RTC_Set_Time(&Status, &RTC_time);
}
else
{
Serial.println("Wifi Connection Failed! Check Wifi Modul!");
while (1) // Will stop there forever (Notification - three short Blinks with LED on pin 13)
{
digitalWrite(13, HIGH);
delay(200);
digitalWrite(13, LOW);
delay(200);
digitalWrite(13, HIGH);
delay(200);
digitalWrite(13, LOW);
delay(200);
digitalWrite(13, HIGH);
delay(200);
digitalWrite(13, LOW);
delay(1500);
}
}
/**** Setting Connection with EEPROM. Will stuck here if the EEPROM Modul is nit connected****/
EEPROM_init(&EEPROM_time);
/**** Beginning of the timer interrupts. No more delays allowed!! ****/
Timer2.setPeriod(1000); // Period 1000 uS = 1 mS ************* CHOOSE RIGHT TIMER FOR ARDUINO MEGA *****************
Timer2.enableISR(CHANNEL_B); // Channel B = Arduino Nano PIN D3 (https://alexgyver.ru/lessons/timer-isr/)
Serial.println("LOOP START");
}
void loop()
{
Lamp_Timer_Routine(&Status, &Ampel);
Pins_Routine(&Ampel);
UART_Routine(&Status, Rec_Line, false);
Time_Routine(&Status, &Clock, &EEPROM_time, &RTC_time);
//********* DEBUG USING A PUSHBUTTON **********
/*if(digitalRead(button) == 0)
{
if(sent == 0)
{
sent = 1;
DS3231_get(&RTC_time);
Serial.println("RTC Time : " + (String)RTC_time.hour + ":" + (String)RTC_time.min);
Serial.print("EEPROM Time: " + (String)EEPROM_time.hours + ":" + (String)EEPROM_time.minutes);
}
}
else
sent = 0;*/
/********* DEBUG USING A PUSHBUTTON **********/
}
/*
* Timer 2 Interrupt Handler. Being activatet once per 1 ms
*/
ISR(TIMER2_B)
{
Status.Lamp_Tim_ChB_Int = 1;
Status.Pins_Tim_ChB_Int = 1;
Status.Clock_Tim_ChB_Int = 1;
//Status.Fan_Tim_ChB_Int = 1;
}
/*
* Traffic light control routine
*/
void Lamp_Timer_Routine(status_flags_t* Status, ampel_t* Ampel)
{
//Serial.println("Lamp_Timer_Routine");
if(Status->Lamp_Tim_ChB_Int == 1)
{
ticks_ampel++;
if(Status->post_request_received == 1) // if we have received a POST request from the Website
{
Serial.print("Request received:");
Serial.println(POST_Request);
for(uint8_t i = 0; i < 5; i++)
{
switch(POST_Request[i])
{
case '0': {Ampel->ein_ampel[i].future_state = ROT;Serial.println("RED");} break;
case '1': {Ampel->ein_ampel[i].future_state = GRUN;Serial.println("GREEN");} break;
}
}
//Send_Apel_State(Ampel);
ticks_ampel = 0; // Resetting the timer to let our combination work one cycle
Status->post_request_received = 0;
}
if(ticks_ampel >= Ampel->main_state_time) // normal (independed) mode of work
{
ticks_ampel = 0;
Ampel->curr_phase = (Ampel->curr_phase+1)% AMOUNT_OF_PHASES ;
switch(Ampel->curr_phase) // here we work with main states of the lights
{
case 0:
{
Ampel->ein_ampel[0].future_state = GRUN;
Ampel->ein_ampel[1].future_state = ROT;
Ampel->ein_ampel[2].future_state = GRUN;
Ampel->ein_ampel[3].future_state = ROT;
Ampel->ein_ampel[4].future_state = ROT;
} break;
case 1:
{
Ampel->ein_ampel[0].future_state = GRUN;
Ampel->ein_ampel[1].future_state = ROT;
Ampel->ein_ampel[2].future_state = ROT;
Ampel->ein_ampel[3].future_state = GRUN;
Ampel->ein_ampel[4].future_state = ROT;
} break;
case 2:
{
Ampel->ein_ampel[0].future_state = ROT;
Ampel->ein_ampel[1].future_state = GRUN;
Ampel->ein_ampel[2].future_state = ROT;
Ampel->ein_ampel[3].future_state = GRUN;
Ampel->ein_ampel[4].future_state = ROT;
} break;
case 3:
{
Ampel->ein_ampel[0].future_state = ROT;
Ampel->ein_ampel[1].future_state = GRUN;
Ampel->ein_ampel[2].future_state = ROT;
Ampel->ein_ampel[3].future_state = ROT;
Ampel->ein_ampel[4].future_state = GRUN;
} break;
case 4:
{
Ampel->ein_ampel[0].future_state = ROT;
Ampel->ein_ampel[1].future_state = ROT;
Ampel->ein_ampel[2].future_state = GRUN;
Ampel->ein_ampel[3].future_state = ROT;
Ampel->ein_ampel[4].future_state = GRUN;
} break;
}
}
for(int i = 0; i < 5; i++)
{
switch(Ampel->ein_ampel[i].future_state)
{
case ROT:
{
switch(Ampel->ein_ampel[i].current_state)
{
case ROT:; break;
case GRUN:
{
Ampel->ein_ampel[i].current_state = GRUN_ROT;
} break;
case GRUN_ROT:
{
if(Ampel->ein_ampel[i].ticks >= Ampel->transit_state_time) // Time check
{
Ampel->ein_ampel[i].current_state = ROT;
Status->post_ready_to_send = 1;
Ampel->ein_ampel[i].ticks = 0; // Stop
}
else Ampel->ein_ampel[i].ticks++;
} break;
case ROT_GRUN:
{
Ampel->ein_ampel[i].current_state = GRUN_ROT;
} break;
}
} break;
case GRUN:
{
switch(Ampel->ein_ampel[i].current_state)
{
case GRUN:; break;
case ROT:
{
Ampel->ein_ampel[i].ticks++; // Delay before switching
if ((uint16_t)Ampel->ein_ampel[i].ticks >= (uint16_t)Ampel->transit_state_time);
{
Ampel->ein_ampel[i].current_state = ROT_GRUN;
Ampel->ein_ampel[i].ticks = 0;
}
} break;
case ROT_GRUN:
{
if(Ampel->ein_ampel[i].ticks >= Ampel->transit_state_time) // Time check
{
Ampel->ein_ampel[i].current_state = GRUN;
Ampel->ein_ampel[i].ticks = 0;
Status->post_ready_to_send = 1;
}
else Ampel->ein_ampel[i].ticks++;
} break;
case GRUN_ROT: break;
//case ALLE_AUS: break;
}
}
}
}
if(Status->post_ready_to_send == 1)
{
Send_Apel_State(Ampel); /********************************************************************************************************************************************/
Status->post_ready_to_send = 0;
}
Status->Lamp_Tim_ChB_Int = 0;
}
}
/*
* Work with output pins (Lamps)
*/
void Pins_Routine(ampel_t* Ampel)
{
//Serial.println("Pins_Routine");
if(Status.Pins_Tim_ChB_Int == 1)
{
for(uint8_t i = 0; i < 5; i++)
{
//Serial.println((String)"Current state of " + i + " is " + Ampel->ein_ampel[i].current_state);
switch(Ampel->ein_ampel[i].current_state)
{
case ROT:
{
digitalWrite(Ampel->ein_ampel[i].rot, HIGH);
digitalWrite(Ampel->ein_ampel[i].gelb, LOW);
digitalWrite(Ampel->ein_ampel[i].grun, LOW);
} break;
case ROT_GRUN:
{
digitalWrite(Ampel->ein_ampel[i].rot, HIGH);
digitalWrite(Ampel->ein_ampel[i].gelb, HIGH);
digitalWrite(Ampel->ein_ampel[i].grun, LOW);
} break;
case GRUN:
{
digitalWrite(Ampel->ein_ampel[i].rot, LOW);
digitalWrite(Ampel->ein_ampel[i].gelb, LOW);
digitalWrite(Ampel->ein_ampel[i].grun, HIGH);
} break;
case GRUN_ROT:
{
digitalWrite(Ampel->ein_ampel[i].rot, LOW);
digitalWrite(Ampel->ein_ampel[i].gelb, HIGH);
digitalWrite(Ampel->ein_ampel[i].grun, LOW);
} break;
}
}
Status.Pins_Tim_ChB_Int =0 ;
}
}
/*
* Receiving of a Command from ESP-01
* Silent - mode of work - with writing the line to Serial monitor (false) or without (true)
*/
void UART_Routine(status_flags_t* Status, char* Line, bool Silent)
{
//Serial.println("UART_Routine");
if(Serial1.available() > 0)
{
Line[i] = Serial1.read();
if(Line[i] == '\n')
{
Line[i-1] = '\0';
Status->debug_flag = 1;
Status->string_received = 1;
i = 0;
}
else
i++;
}
if(Status->string_received == 1)
{
if(Silent == false)
{
Serial.println("From ESP-01: " + (String)Line);
}
if((Line[0] == 'r') && (Line[1] == 'c') && (Line[2] == 'w') && (Line[3] == 'd') && isDigit(Line[4]) && isDigit(Line[5]) && isDigit(Line[6]) && (isDigit(Line[7])) && isDigit(Line[8]))
{
POST_Request[0] = Line[4];
POST_Request[1] = Line[5];
POST_Request[2] = Line[6];
POST_Request[3] = Line[7];
POST_Request[4] = Line[8];
Status->post_request_received = 1;
}
Status->string_received = 0;
}
}
/*
* Routine for moving Clock arrows. (Calculating the amount and implementing of ticks)
* Task: Calculate ticks-> implement ticks -> save the time from RTC into the Memory
*/
void Time_Routine(status_flags_t* Status, my_clock_t* Clock, my_time_t* EEPROM_time, ts* RTC_time)
{
if(Status->Clock_Tim_ChB_Int == 1)
{
ticks_upd_clock++;
if(ticks_upd_clock >= CLOCK_SYNCH_TIME_PERIOD) // Activation once per 5000 ms to synchronize actual Time and Time on clocks (to calculate the amount of "steps" for the minute arrow)
{
DS3231_get(RTC_time); // Only this time reading is needed. Structure with EEPROM Time is Already written during initialisation
int16_t calculated_ticks = calculate_Ticks(RTC_time, EEPROM_time);
Serial.print("\tCalculated ticks: ");
Serial.println(calculated_ticks);
Clock->ticks_to_do += calculated_ticks;
Serial.print("Ticks to do updated: ");
Serial.println(Clock->ticks_to_do);
ticks_upd_clock = 0;
}
if(Clock->ticks_to_do > 0) // if there are steps to do
{
Clock->stopped = false;
if((Clock->ticks_timer <= Clock->impulse_period) && (Clock->tick_in_progress == false)) // Start impulse immediately // condition changed
{
Clock->tick_in_progress = true;
do_impulse(Clock);
}
Clock->ticks_timer++;
if((Clock->ticks_timer >= Clock->impulse_period) && (Clock->ticks_timer <= (Clock->tick_period))) // Pause between impulses
{
stop_clock(Clock);
}
if(Clock->ticks_timer >= Clock->tick_period) // Impulse is done
{
Clock->ticks_to_do--;
Clock->ticks_timer = 0;
Clock->tick_in_progress = false; // allows us to start the next tick
}
}
else
{
if((Clock->ticks_to_do <= 0) && (Clock->stopped == false)) // such condition only to prevent reentering the piece
{
Clock->stopped = true;
Serial.println("Clock stopped");
stop_clock(Clock);
EEPROM_save(EEPROM_time, RTC_time);
Serial.println("Memory written: Block: " + (String)EEPROM_time->offset + "\tHours: " + (String)EEPROM_time->hours + "\tMinutes: " + (String)EEPROM_time->minutes + "\tRewrite counter: " + (String)EEPROM_time->memorycell_write_counter);
Clock->ticks_timer = 0;
}
}
Status->Clock_Tim_ChB_Int = 0;
}
}
/*
* Fan (Ventilator, Cooler) control
* in this version - NO Thermosensor. Starting of the fan id blind
*/
/*
void Fan_Routine(status_flags_t* Status)
{
if(Status->Fan_Tim_ChB_Int == 1)
{
if(Status->Fan_Started == 1) // if What?
{
}
Status->Fan_Tim_ChB_Int = 0;
}
}*/